package de.lgohlke.vo;

import org.apache.commons.collections.BufferUnderflowException;

// TODO mal schauen, ob es irgendwo eine schnellere Implementierung gibt, Google oder so
/**
 * <p>RingBuffer class.</p>
 *
 * @author lars
 * @version $Id: $
 */
public class RingBuffer<Item> // implements Iterable<Item>
{
  private final Item[] a;        // queue elements
  private int          N     = 0; // number of elements on queue
  private int          first = 0; // index of first element of queue
  private int          last  = 0; // index of next available slot

  // cast needed since no generic array creation in Java
  /**
   * <p>Constructor for RingBuffer.</p>
   *
   * @param capacity a int.
   * @param <Item> a Item object.
   */
  @SuppressWarnings("unchecked")
  public RingBuffer(final int capacity)
  {
    a = (Item[]) new Object[capacity];
  }

  /**
   * <p>isEmpty.</p>
   *
   * @return a boolean.
   */
  public boolean isEmpty()
  {
    return N == 0;
  }

  /**
   * <p>size.</p>
   *
   * @return a int.
   */
  public int size()
  {
    return N;
  }

  /**
   * <p>getArray.</p>
   *
   * @return an array of Item objects.
   */
  public Item[] getArray()
  {
    return a.clone();
  }

  /**
   * <p>enqueue.</p>
   *
   * @param item a Item object.
   */
  public void enqueue(final Item item)
  {
    N = N == a.length ? N : N + 1;

    // System.out.println("enqueue : " + item);
    a[last] = item;
    last = (last + 1) % a.length; // wrap-around
  }

  // remove the least recently added item - doesn't check for underflow
  /**
   * <p>dequeue.</p>
   *
   * @return a Item object.
   */
  public Item dequeue()
  {
    if (isEmpty())
    {
      throw new BufferUnderflowException("Ring buffer underflow");
    }
    Item item = a[first];
    a[first] = null; // to help with garbage collection
    N--;
    first = (first + 1) % a.length; // wrap-around
    return item;
  }

  // public Iterator<Item> iterator()
  // {
  // return new RingBufferIterator();
  // }

  // // an iterator, doesn't implement remove() since it's optional
  // private class RingBufferIterator implements Iterator<Item>
  // {
  // private int i = 0;
  //
  // public boolean hasNext()
  // {
  // return i < N;
  // }
  //
  // public void remove()
  // {
  // throw new UnsupportedOperationException();
  // }
  //
  // public Item next()
  // {
  // if (!hasNext())
  // throw new NoSuchElementException();
  // return a[i++];
  // }
  // }

  /**
   * immer innerhalb der Grenzen durch modulo
   *
   * @param i a int.
   * @return a Item object.
   */
  public Item get(final int i)
  {
    return a[i % a.length];
  }

  // a test client
  /**
   * <p>main.</p>
   *
   * @param args an array of {@link java.lang.String} objects.
   */
  public static void main(final String[] args)
  {
    RingBuffer<String> ring = new RingBuffer<String>(3);
    ring.enqueue("This");
    ring.enqueue("is");
    ring.enqueue("a");
    ring.enqueue("test.");

    // System.out.println("---");
    // System.out.println(ring.size());

    for (int i = 0; i < ring.size(); i++)
    {
      System.out.println(ring.get(i));
    }

    System.out.println();

    while (!ring.isEmpty())
    {
      System.out.println(ring.dequeue());
    }
  }

  /**
   * <p>isFilled.</p>
   *
   * @return a boolean.
   */
  public boolean isFilled()
  {
    return N == a.length;
  }

  /** {@inheritDoc} */
  @Override
  public String toString()
  {
    StringBuffer sb = new StringBuffer();

    for (int i = 0; i < size(); i++)
    {
      sb.append("[" + i + "] " + get(i) + "\n");
    }
    return sb.toString();
  }
}
